﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Antlr3.ST;
using System.Reflection;
using System.IO;
using com.guysherman.simulclass;

namespace com.guysherman.simulclass
{
    public class ScannedClass
    {
        private string preamble { get; set; }
        private string globalIncludeFile { get; set; }
        private string bodyInclude { get; set; }
        private Type subjectType { get; set; }
       

        private List<ScannedMember> members;

        public IEnumerable<ScannedMethod> Methods
        {
            get
            {
                return (from member in members
                        where (member is ScannedMethod)
                        select member).Cast<ScannedMethod>();
            }
        }

        public IEnumerable<ScannedField> Fields
        {
            get
            {
                return (from member in members
                        where (member is ScannedField)
                        select member).Cast<ScannedField>();
            }
        }

        

        public ScannedClass(Type subjectType, string preamble, string globalInclude, string bodyInclude )
        {

            this.subjectType = subjectType;
            this.members = new List<ScannedMember>();
            this.preamble = preamble;
            this.globalIncludeFile = globalInclude;
            this.bodyInclude = bodyInclude;


            MemberInfo[] memberInfos = subjectType.GetMembers(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

            ParallelQuery<ScannedField> fields = from member in memberInfos.AsParallel()
                                                 where member.MemberType == MemberTypes.Field && !(member.Name.Contains("BackingField")) && !(member.Name.Contains("<") || member.Name.Contains(">"))
                                                 select new ScannedField
                                                 {
                                                     MemberKind = member.MemberType,
                                                     MemberType = (member as FieldInfo).FieldType,
                                                     Name = member.Name,
                                                     Visibility = (member as FieldInfo).Visibility(),
                                                     ClassName = this.subjectType.Name
                                                 };

            this.members.AddRange(fields.ToArray<ScannedField>());

            ParallelQuery<ScannedMethod> methods = from member in memberInfos.AsParallel()
                                                   where member.MemberType == MemberTypes.Method && !(member.Name.Contains("<") || member.Name.Contains(">"))
                                                   select new ScannedMethod
                                                   {
                                                       MemberKind = member.MemberType,
                                                       MemberType = (member as MethodInfo).ReturnType,
                                                       MemberMethodInfo = member as MethodInfo,
                                                       Name = member.Name,
                                                       Visibility = (member as MethodInfo).Visibility(),
                                                       ClassName = this.subjectType.Name
                                                   };

            this.members.AddRange(methods.ToArray<ScannedMethod>());

            

            
        }

        public string Header()
        {
            StringBuilder header = new StringBuilder();
            string namespaceContent = string.Empty;
            StringTemplateGroup group = new StringTemplateGroup(new StringReader(Templates.HeaderTemplate));

            StringTemplate wrapperClass = group.GetInstanceOf("wrapperClass");
            wrapperClass.SetAttribute("namespace", this.subjectType.Namespace);
            wrapperClass.SetAttribute("name", this.subjectType.Name);
            wrapperClass.SetAttribute("memberfunctions", memberFunctions());
            wrapperClass.SetAttribute("monoclassfields", monoClassFields());
            wrapperClass.SetAttribute("monomethods", monoMethods());
            wrapperClass.SetAttribute("monomethodsforproperties", string.Empty);
            namespaceContent = wrapperClass.ToString();

            string[] namespaceComponents = this.subjectType.Namespace.Split(".".ToCharArray());
            
            StringTemplate namespaceDefinition = group.GetInstanceOf("namespaceDefinition");

            foreach (string nameSpace in namespaceComponents.Reverse<string>())
            {
                namespaceDefinition.SetAttribute("namespaceName", nameSpace);
                namespaceDefinition.SetAttribute("namespaceContent", namespaceContent);
                namespaceContent = namespaceDefinition.ToString();
                namespaceDefinition.Reset();
            }

            StringTemplate headerDefinition = group.GetInstanceOf("headerDefinition");
            headerDefinition.SetAttribute("globalInclude", globalIncludeFile);
            headerDefinition.SetAttribute("headerContent", namespaceContent);
            namespaceContent = headerDefinition.ToString();

            header.AppendLine(preamble);
            header.AppendLine(namespaceContent);

            return header.ToString();
        }

        public string Definition()
        {
            StringBuilder definition = new StringBuilder();
            definition.AppendLine(bodyInclude);
            definition.AppendLine(string.Format("#include \"{0}.h\"", subjectType.Name));
            definition.AppendLine();
            
            string[] namespaceComponents = this.subjectType.Namespace.Split(".".ToCharArray());
            foreach (string namespaceComponent in namespaceComponents)
            {
                definition.AppendLine(string.Format("using namespace {0};", namespaceComponent));
            }

            definition.AppendLine();

            definition.AppendLine(constructorDestructor());

            definition.AppendLine(memberFunctionDefinitions());

            return definition.ToString();

        }

        private string constructorDestructor()
        {
            

            StringTemplateGroup group = new StringTemplateGroup(new StringReader(Templates.HeaderTemplate));

            StringTemplate cdTemplate = group.GetInstanceOf("constructorDestructorDefinition");

            cdTemplate.SetAttribute("className", this.subjectType.Name);
            cdTemplate.SetAttribute("namespace", this.subjectType.Namespace);

            IEnumerable<ScannedMember> scannedMethods = from member in this.members
                                                        where member.MemberKind == MemberTypes.Method
                                                        select member;
            foreach (ScannedMethod method in scannedMethods)
            {

                StringTemplate getMethodTemplate = group.GetInstanceOf("getMethodTemplate");
                ParameterInfo[] parameters = method.MemberMethodInfo.GetParameters();
                
                getMethodTemplate.SetAttribute("name", method.Name);
                getMethodTemplate.SetAttribute("argCount", parameters.Length);
                cdTemplate.SetAttribute("methods", getMethodTemplate.ToString());
            }

            IEnumerable<ScannedMember> scannedFields = from member in this.members
                                                       where member.MemberKind == MemberTypes.Field
                                                       select member;
            foreach (ScannedField field in scannedFields)
            {
                StringTemplate getFieldTemplate = group.GetInstanceOf("getFieldTemplate");
                getFieldTemplate.SetAttribute("name", field.Name);
                cdTemplate.SetAttribute("fields", getFieldTemplate.ToString());
            }

            return cdTemplate.ToString();

        }

        private string memberFunctionDefinitions()
        {
            StringBuilder memberFunctionsSb = new StringBuilder();

            foreach (ScannedMember member in members)
            {
                memberFunctionsSb.AppendLine(member.MemberFunctionDefinition());
            }

            return memberFunctionsSb.ToString();
        }

        private object monoMethods()
        {
            StringBuilder monoMethodsSb = new StringBuilder();

            IEnumerable<ScannedMember> scannedMethods = from member in this.members
                                                       where member.MemberKind == MemberTypes.Method
                                                       select member;
            foreach (ScannedMethod method in scannedMethods)
            {
                monoMethodsSb.AppendLine(method.MemberVariableDeclaration());
            }

            return monoMethodsSb.ToString();
        }

        private object monoClassFields()
        {
            StringBuilder monoClassFieldsSb = new StringBuilder();

            IEnumerable<ScannedMember> scannedFields = from member in this.members
                                                       where member.MemberKind == MemberTypes.Field
                                                       select member;
            foreach (ScannedField field in scannedFields)
            {
                monoClassFieldsSb.AppendLine(field.MemberVariableDeclaration());
            }

            return monoClassFieldsSb.ToString();
        }

        private string memberFunctions()
        {
            StringBuilder memberFunctionsSb = new StringBuilder();
            
            foreach (ScannedMember member in members)
            {
                memberFunctionsSb.AppendLine(member.MemberFunctionDeclaration());
            }

            return memberFunctionsSb.ToString();
        }



        
    }
}
